#include "core_pch.h"
#include "RTTI.h"
#include "Res.h"
#include "Utility.h"
#include "DString.h"
#include "SString.h"
#include "RawBuf.h"

inline void Obj_StaticInit()
{
	ClassDef* cls = Obj::RTTI();
	cls->PropIndexStart = 0;
	cls->BindFuncIndexStart = 0;
}

Base_BeginObj( Res)

ClassDef* cls = Res::RTTI();
cls->InitBegin();
{
	static PropDef def("Name", Data_SString, DataOffset( Res, mName));
	cls->_defProp( &def);
}

EndObj;



























////////////////Pack////////////////
Ins_NoCons( Pack);
Begin_Cons_Init( Pack)
,mIsSaveing( false)
Begin_Data_Init( Pack)

ClassDef* cls = Res::RTTI();
cls->InitBegin();
{
	static PropDef def("Name", Data_SString, DataOffset( Pack, mName));
	cls->_defProp( &def);
}
{
	static PropDef def("ObjList", Data_RefObj_Ptr, DataOffset( Pack, mObjDatas));
	def.IsArray = true;
	cls->_defProp( &def);
}
{
	static PropDef def("ResList", Data_RefObj_Ptr, DataOffset( Pack, mResList));
	def.IsArray = true;
	cls->_defProp( &def);
}

EndObj;

Pack::Pack( const char* name)
:mName( name)
,mIsSaveing( false)
{
}


void Pack::_writeRaw( void* data, s32_t count)
{
	_check( !mDataStream.isNull());
	_check( mIsSaveing);
	mDataStream->write( data, count);

}
void Pack::_readRaw( void* data, s32_t count)
{
	_check( !mIsSaveing);
	_check( !mDataStream.isNull());
	mDataStream->read( data, count);

}

void Pack::_writeString( const char* data)
{
	_check( mIsSaveing);
	_check( !mDataStream.isNull());
	s32_t num = strlen( data);
	mDataStream->write( &num, sizeof( num));
	if( num)
	{
		mDataStream->write( data, num);
	}
}

ds_t Pack::_readString()
{
	_check( !mIsSaveing);
	_check( !mDataStream.isNull());
	ds_t ret;
	s32_t num = 0;
	mDataStream->read( &num, sizeof( num));
	if( num)
	{
		char* buf = (char*)coreAlloc( num+1);
		mDataStream->read( buf, num);
		buf[num] = '\0';
		ret = buf;
		coreFree( buf);
	}

	return ret;
}


void Pack::_regLinkObj( Obj* obj)
{
	if( mStreamObjList.FindIndex( obj) != null_index)
	{
		return;
	}

	ClassDef* clsDef = obj->GetClass();
	if( clsDef->IsTypeOf( Pack::RTTI()))
	{
		_check( obj == this);
		mStreamObjList.Add( obj);
	}
	else if( clsDef->IsTypeOf( Res::RTTI()))
	{
		Res* res = (Res*)obj;

		if( res->mPack == this)
		{
			mStreamObjList.Add( obj);
		}
		else
		{
			return;
		}
	}
	else
	{
		mStreamObjList.Add( obj);
	}


	if( obj == 0)
	{
		return;
	}

	// object super class list
	TArray< ClassDef* > clsList;
	obj->GetClass()->GetInheritClassList( clsList);


	TArray< ClassDef* >::Iterator inherIt( clsList);
	while( inherIt)
	{
		ClassDef* loadType = *inherIt;
		inherIt.Next();

		TArray< PropDef*>::Iterator dataIt = loadType->PropIt();
		while( dataIt)
		{
			PropDef* dataDef = *dataIt;
			dataIt.Next();

			PropData dataVal;
			obj->GetMemberData( dataDef, dataVal);

			if( dataDef->Type == Data_RefObj_Ptr)// all data objs
			{
				s32_t size = dataVal.Num();
				for( s32_t i = 0; i < size; ++i)
				{
					P(Obj)* dataObj = (P(Obj)*)dataVal[i];
					_regLinkObj( *dataObj);
				}
			}
			else if( dataDef->Type == Data_Struct_Ins)// all data objs
			{
				s32_t stcNum = dataVal.Num();
				StructDef* sd = dataDef->StructType;
				for( s32_t s = 0; s< stcNum; ++s)
				{
					void* stcPtr = dataVal[s];
					for( s32_t i = 0; i < sd->Props.Num(); ++i)
					{
						PropData stcPropData;
						sd->GetMemberData( stcPtr, sd->Props[i], stcPropData);

						for( s32_t o =  0; o < stcPropData.Num(); ++o)
						{
							P(Obj)* dataObj = (P(Obj)*)stcPropData[o];
							_regLinkObj( *dataObj);
						}
					}
				}
			}
		}
	}
}

void Pack::_writeProp( PropData& data)
{
	PropDef* def = data.Def;
	s32_t num = data.Num();
	if( def->IsArray)
	{
		_writeRaw( &num, sizeof(num));
	}

	if( Data_SString == def->Type)
	{
		for( s32_t i = 0; i < num; ++i)
		{
			ss_t& str = *(ss_t*)data[i];
			_writeString( str);
		}
	}
	else if( Data_DString == def->Type)
	{
		for( s32_t i = 0; i < num; ++i)
		{
			ds_t& str = *(ds_t*)data[i];
			_writeString( str);
		}
	}
	else if( Data_Struct_Ins == def->Type)
	{
		for( s32_t i = 0; i < num; ++i)
		{
			void* stcPtr = data[i];

			StructDef* stcDef = def->StructType;
			_check( stcDef);

			for( s32_t p = 0; p < stcDef->Props.Num(); ++p)
			{
				PropData data;
				stcDef->GetMemberData( stcPtr, stcDef->Props[p], data);
				_writeProp( data);
			}
		}
	}
	else if( Data_RefObj_Ptr == def->Type)
	{
		for( s32_t i = 0; i < num; ++i)
		{
			P(Obj)& objPtr = *(P(Obj)*)data[i];
			s32_t linkIndex = mStreamObjList.FindIndex( objPtr);
			_writeRaw( &linkIndex, sizeof(linkIndex));

			if( linkIndex == null_index)// outpack res
			{
				_check( objPtr->GetClass()->IsTypeOf( Res::RTTI()));

				Res* resPtr = (Res*)(Obj*)objPtr;
				s32_t resID = resPtr->mResID;
				_writeRaw( &resID, sizeof( resID));

				_check( resPtr->mPack != this);
				_writeString( resPtr->mPack->mName);
			}
		}
	}
	else
	{
		if( num)
		{
			//raw array
			s32_t itemSize = RawTypeSize( def->Type);
			RawBuf& buf = *(RawBuf*)data.Data;
			_writeRaw( buf.Data(), buf.Num() * itemSize);
		}
	}
}

void Pack::_writeObj( Obj* objPtr)
{
	bool isNull = objPtr == 0;
	_writeRaw( &isNull, sizeof( isNull));
	if( isNull)
	{
		return;
	}

	ClassDef* def = objPtr->GetClass();
	_writeString( def->Name);

	objPtr->OnStream( this);

	// object super class list
	TArray< ClassDef* > clsList;
	def->GetInheritClassList( clsList);

	TArray< ClassDef* >::Iterator inherIt( clsList);
	while( inherIt)
	{
		ClassDef* loadType = *inherIt;
		inherIt.Next();

		TArray< PropDef*>::Iterator dataIt = loadType->PropIt();
		while( dataIt)
		{
			PropDef* dataDef = *dataIt;
			dataIt.Next();

			PropData dataVal;
			objPtr->GetMemberData( dataDef, dataVal);
			_writeProp( dataVal);
		}
	}
}



s32_t Pack::_freeResSlot()
{
	s32_t num = mResList.Num();
	for( s32_t i = 0 ; i < num; ++i)
	{
		if( mResList[i] == 0)
		{
			return i;
		}
	}

	mResList.AddNew();
	return num;
}


Res* Pack::NewRes( ClassDef* cls)
{
	if(!cls->IsTypeOf( Res::RTTI()))
	{
		assert(false);
		return 0;
	}


	Res* res = (Res*)cls->New();
	res->mPack = this;
	s32_t resId = _freeResSlot();
	res->mResID = resId;

	mResList[resId] = res;

	return res;
}

void Pack::DelRes( Res* res)
{
	_check( mResList[ res->mResID ] == res);
	mResList[ res->mResID] = 0;
}

Res* Pack::GetRes( s32_t resID)
{
	return mResList[ resID];
}

Ogre::DataStreamPtr _openSaveData(const char* pFileName)
{
	FILE* pFile = 0;
#ifdef _MSC_VER
	errno_t uiError = fopen_s(&pFile, pFileName, "wb");
	if( uiError == 0 )
	{
	}
	else
	{
		pFile = 0;
	}
#else
	fopen(pFileName, "wb");
#endif

	return Ogre::DataStreamPtr (OGRE_NEW Ogre::FileHandleDataStream( pFile, Ogre::DataStream::WRITE));
}

void Pack::Save( const char* dstPath)
{
	ds_t fileName = dstPath;
	fileName.Replace("\\", "/");
	if( fileName[ fileName.Num() -1] != '/')
	{
		fileName += '/';
	}

	fileName += mName;
	fileName += ".pck";

	mDataStream = _openSaveData( fileName);
	mIsSaveing = true;

	mStreamObjList.Clear();

	_regLinkObj( this);

	s32_t ver = 0;
	_writeRaw( &ver, sizeof(ver));

	// go over all object
	TArray< Obj::Ptr>::Iterator itSaveObj( mStreamObjList);

	s32_t num = mStreamObjList.Num();
	_writeRaw( &num, sizeof(num));

	while( itSaveObj)
	{
		_writeObj( *itSaveObj);
		itSaveObj.Next();
	}

	mDataStream->close();
	mDataStream.setNull();
}



void Pack::InsertObj( Obj* obj)
{
	mObjDatas.Add( obj);
}


Obj* Pack::GetObj( s32_t index)
{
	return mObjDatas[ index];
}


void InitCoreRTTI()
{
	Obj_StaticInit();
	Res_StaticInit();
	Pack_StaticInit();
}
